package com.esun.server.aspect;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSONObject;
import com.esun.common.utils.IpUtil;
import com.esun.dao.das.OperateLogDas;
import com.esun.dao.eo.OperateLogEo;
import com.esun.server.annotation.OptLog;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 操作日志
 *
 * @author zzg
 * @since 2021/6/30
 */
@Aspect
@Component
public class OptLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(OptLogAspect.class);
    private final ThreadLocal<OperateLogEo> logThreadLocal = new ThreadLocal<>();
    @Resource
    private OperateLogDas operateLogDas;

    @Pointcut("execution(public * com.esun.server.controller..*(..))")
    public void systemLog() {
    }

    @Around(value = "systemLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = methodSignature.getMethod();
        OperateLogEo log = new OperateLogEo();
        Object[] args = joinPoint.getArgs();
        List<Object> list = new ArrayList<>();
        if (args.length > 0) {
            for (Object object : args) {
                if (object instanceof MultipartFile) {
                    JSONObject json = new JSONObject();
                    json.put("fileName", ((MultipartFile) object).getName());
                    list.add(json);
                } else {
                    list.add(object);
                }
            }
            String param = JSONObject.toJSON(list).toString();
            log.setRequestParam(param);
        }
        String ip = IpUtil.getClientIp(request);
        log.setIp(ip);
        log.setMethod(targetMethod.getName());
        log.setRequestType(request.getMethod());
        log.setRequestUrl(request.getRequestURL().toString());
        if (targetMethod.isAnnotationPresent(OptLog.class)) {
            OptLog optLog = targetMethod.getAnnotation(OptLog.class);
            log.setModule(optLog.module());
            log.setDescription(optLog.desc());
        }
        logThreadLocal.set(log);
        //记录用户ID
//        String userId = String.valueOf(StpUtil.getLoginId());
//        log.setUserId(userId);
        //1、记录执行时间
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed(joinPoint.getArgs());
        long endTime = System.currentTimeMillis();
        long totalTime = endTime - startTime;
        log.setCostTime(totalTime);
        log.setResponseData(JSONObject.toJSONString(result));
        logThreadLocal.set(log);
        try {
            operateLogDas.insert(log);
        } catch (Exception exception) {
            logger.error("保存操作日志出错：", exception);
        } finally {
            logThreadLocal.remove();
        }
        return result;
    }

    @AfterThrowing(pointcut = "systemLog()", throwing = "e")
    public void handle(JoinPoint point, Exception e) {
        String exception = stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace());
        OperateLogEo log = logThreadLocal.get();
        if (log != null) {
            int len = 2000;
            if (StringUtils.isNotEmpty(exception) && exception.length() > len) {
                exception = exception.substring(0, len);
            }
            log.setException(exception);
            try {
                operateLogDas.insert(log);
            } catch (Exception ex) {
                logger.error("保存操作日志出错：", ex);
            } finally {
                logThreadLocal.remove();
            }
        }
    }

    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuilder strbuff = new StringBuilder();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet).append("\n");
        }
        return exceptionName + ":" + exceptionMessage + "\n\t" + strbuff;
    }
}
